home *** CD-ROM | disk | FTP | other *** search
/ Aminet 43 / Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso / Aminet / util / arc / CheckX.lha / CheckX / sources / CheckX.c next >
Encoding:
C/C++ Source or Header  |  2001-04-18  |  46.9 KB  |  1,591 lines

  1. #define NAME         "CheckX"
  2. #define REVISION     "84"
  3. #define DISTRIBUTION "(Freeware) "
  4. #define DATE         "18.04.2001"
  5.  
  6. /*
  7. This program scans for crunched, linked files and archived files. It
  8. decrunches them and saves the result files to another directory-tree.
  9. The scanning routines are recursive and thus check really all stuff.
  10.  
  11. The program must be compiled and linked without startup-code. You can set
  12. the pure file protection bit and make it resident, because it is multi-
  13. reentrant (no global variables, except library bases).
  14. */
  15.  
  16. /* Programmheader
  17.  
  18.     Name:        CheckX
  19.     Author:        SDI
  20.     Distribution:    Freeware
  21.     Description:    scans, decrunches and dearchives files
  22.     Compileropts:    -
  23.     Linkeropts:    -l xpkmaster xadmaster amiga -gsi
  24.  
  25.  1.0   14.12.96 : first Version
  26.  1.1   28.12.96 : moved PassRequest into xpkmaster.library
  27.  1.2   12.02.97 : now also decrunches Exe-Files
  28.  1.3   15.06.97 : added length output as test
  29.  1.4   21.11.97 : renamed from Decrunch, got really new program
  30.  1.5   22.11.97 : bug-fixes
  31.  1.6   29.11.97 : added unarchiving feature
  32.  1.7   30.11.97 : bug-fixes
  33.  1.8   06.12.97 : xpkmaster.library now only required with ASKPWD option
  34.  1.9   07.12.97 : Added archive copy for weird archive names, better error
  35.     codes
  36.  1.10  08.12.97 : fixed error codes a bit
  37.  1.11  11.12.97 : disabled DOS requests, added Zip-Archives, added TaskID
  38.     to temporary filenames
  39.  1.12  12.12.97 : added Arc, ZOO and LhASFX archives
  40.  1.13  13.12.97 : fixed Arc recognition
  41.  1.14  19.12.97 : deletes copied arc before scan
  42.  1.15  22.12.97 : crunched archives are unarchived correctly now
  43.  1.16  02.01.98 : opens dos.library itself, no startup-code required
  44.  1.17  23.01.98 : added disk crunchers
  45.  1.18  24.01.98 : some fixes
  46.  1.19  01.02.98 : little bug-fix in argument-option use
  47.  1.20  06.02.98 : better error output, added automount
  48.  1.21  08.02.98 : little bug-fix
  49.  1.22  10.02.98 : fixed archive copy conditions, added PRINTALL
  50.  1.23  12.02.98 : fixed help text, bug fixes with unlinking and FreeMem
  51.  1.24  13.02.98 : added unstripping
  52.  1.25  04.03.98 : added PRINTEXEC
  53.  1.26  13.03.98 : added high density DMS support
  54.  1.27  19.03.98 : added LOUD keyword
  55.  1.28  23.03.98 : RDx no longer depends on archive depth, but on dddepth
  56.  1.29  10.04.98 : bug fixes
  57.  1.30  26.04.98 : bug fixes
  58.  1.31  09.05.98 : now uses no longer adress 4 for SysBase
  59.  1.32  31.05.98 : better output
  60.  1.33  04.06.98 : added HEADER addition for address files
  61.  1.34  08.08.98 : bug fix with SAVE
  62.  1.35  24.09.98 : added xvs.library virus checks
  63.  1.36  18.10.98 : xvs is opened global and only once
  64.  1.37  30.10.98 : renamed from CheckXFD
  65.  1.38  11.11.98 : fixed format drive bug using a delay and an error report
  66.  1.39  14.11.98 : format error with AUTOMOUNT removed
  67.  1.40  16.11.98 : removed HEADER addition stuff
  68.  1.41  18.11.98 : better RDx: access and mount
  69.  1.42  23.11.98 : fixed bug with hunk stripping
  70.  1.43  29.12.98 : now prints an error, when virus detection is turned off,
  71.     added time calculation and output
  72.  1.44  31.12.98 : little bug fix
  73.  1.45  06.02.99 : bug fixes, added xadmaster.library stuff, removed LOUD
  74.     and internal DMS call
  75.  1.46  09.02.99 : removed internal LZX call
  76.  1.47  11.02.99 : now uses assembler startcode allocating a bigger stack
  77.  1.48  14.02.99 : bug-fixes for nocylinder archivers (PackDev)
  78.  1.49  16.02.99 : fixed strip option (don't know, where the error was :-)
  79.  1.50  21.02.99 : removed internal Zoom call and disk-archiver stuff
  80.  1.51  22.02.99 : fixed archiver call for remaining non-XAD archivers, added
  81.     empty file check
  82.  1.52  23.02.99 : fixed StartCode return value
  83.  1.53  24.02.99 : forgot empty check for archived files
  84.  1.54  08.03.99 : old archiver calling did not work, when file was XAD
  85.     unarchived
  86.  1.55  26.03.99 : prints error, when logfile cannot be created
  87.  1.56  30.03.99 : added bootblock scanning for unarchived disks
  88.  1.57  16.05.99 : bug fix with unlinked file save
  89.  1.58  17.07.99 : removed internal LhA Support
  90.  1.59  30.07.99 : bug fix with file name prints
  91.  1.60  03.08.99 : added DEBUG option
  92.  1.61  05.08.99 : added XVS SelfTest and MemoryTest
  93.  1.62  19.08.99 : added error summary, again fixed name problem
  94.  1.63  14.09.99 : solved big memory loss problem (did not free xadArchiveInfo),
  95.     added logfile comment
  96.  1.64  17.10.99 : removed external archiver calls, added QUIET
  97.  1.65  24.11.99 : now sets nice return values
  98.  1.66  26.11.99 : tries reading again before giving read error
  99.  1.67  03.12.99 : bug fix in name printing
  100.  1.68  04.12.99 : tries again opening files.
  101.  1.69  16.12.99 : added NOSILENT Option
  102.  1.70  15.01.00 : files from disc archives are extracted directly now,
  103.     removed double read tries
  104.  1.71  16.01.00 : fixed error messages for disk unarchiving
  105.  1.72  12.02.00 : added support for ADF images
  106.  1.73  13.02.00 : bug fix for disk archives
  107.  1.74  09.03.00 : now detects XADFIF_NOFILENAME and XADFIF_NOUNCRUNCHSIZE
  108.  1.75  05.04.00 : fixed bug with XADFIF_NOFILENAME
  109.  1.76  26.04.00 : added size checkout for XADFIF_NOUNCRUNCHSIZE
  110.  1.77  05.06.00 : added support for multiple filesystems, removed NODOS and
  111.     diskerr errors, always prints FS-type now.
  112.  1.78  24.07.00 : bug fix
  113.  1.79  27.07.00 : bug fix with multi FS support
  114.  1.80  14.09.00 : added SAVEALL keyword
  115.  1.81  25.03.01 : added support for crypted archives and large archive scan
  116.  1.82  05.04.01 : fixed Enforcer hit
  117.  1.83  08.04.01 : empty file got warning
  118.  1.84  18.04.01 : added texts in case libraries could not be opened
  119. */
  120.  
  121. #include <proto/exec.h>
  122. #include <proto/dos.h>
  123. #include <proto/xfdmaster.h>
  124. #include <proto/xadmaster.h>
  125. #include <proto/xpkmaster.h>
  126. #include <proto/intuition.h>
  127. #include <proto/xvs.h>
  128. #include <proto/utility.h>
  129. #include <libraries/xfdmaster.h>
  130. #include <dos/dostags.h>
  131. #include <dos/doshunks.h>
  132. #include <dos/filehandler.h>
  133. #include <exec/memory.h>
  134. #include "SDI_version.h" /* make version string */
  135. #include "SDI_compiler.h"
  136. #define SDI_TO_ANSI
  137. #include "SDI_ASM_STD_protos.h"
  138.  
  139. #define PARAM   "FROM,LOG,SAVE/K,ALL/S,ASKPWD/S,PRINTALL/S,PRINTEXEC/S," \
  140.         "NODECRUNCH/S,NOUNLINK/S,NOUNARCHIVE/S,NOUNTRACK/S,"     \
  141.         "NOSILENT/S,NOSTRIP/S,NOVIRUS/S,DEBUG/S,QUIET/S,SAVEALL/S"
  142.  
  143. void RawPutChar(ULONG c);
  144.  
  145. #ifdef __SASC
  146.   #pragma libcall SysBase RawPutChar 204 001
  147. #elif defined(__GNUC__)
  148.   #define RawPutChar(c) LP1NR(204, RawPutChar, ULONG, c, d0, , SysBase)
  149. #else
  150.   #pragma amicall(SysBase,0x204,RawPutChar(d0))
  151. #endif
  152.  
  153. ASM(void) KPutC(REG(d0, ULONG c))
  154. {
  155.   RawPutChar(c);
  156. }
  157.  
  158. void KPrintf(STRPTR fmt, ...)
  159. {
  160.   RawDoFmt(fmt, &fmt + 1, (void (*)()) KPutC, 0);
  161. }
  162.  
  163. #ifdef __SASC
  164.   #define XpkBase    xpkbase
  165.   #define ASSIGN_XPK
  166.   #define IntuitionBase    intuitionbase
  167.   #define ASSIGN_INT
  168.   #define UtilityBase    utilitybase
  169.   #define ASSIGN_UTIL
  170. #else
  171.   struct Library *    XpkBase        = 0;
  172.   struct IntuitionBase *IntuitionBase    = 0;
  173.   struct UtilityBase *  UtilityBase     = 0;
  174.   #define ASSIGN_XPK    XpkBase = xpkbase;
  175.   #define ASSIGN_INT    IntuitionBase = intuitionbase;
  176.   #define ASSIGN_UTIL    UtilityBase = utilitybase;
  177. #endif
  178. struct xfdMasterBase *    xfdMasterBase    = 0;
  179. struct DosLibrary *    DOSBase        = 0;
  180. struct ExecBase *    SysBase        = 0;
  181. struct xvsBase *    xvsBase        = 0;
  182. struct xadMasterBase *    xadMasterBase    = 0;
  183.  
  184. struct Args {
  185.   STRPTR from;
  186.   STRPTR log;
  187.   STRPTR save;
  188.   ULONG  all;
  189.   ULONG  askpwd;
  190.   ULONG  printall;
  191.   ULONG  printexec;
  192.   ULONG  nodecrunch;
  193.   ULONG  nounlink;
  194.   ULONG  nounarchive;
  195.   ULONG  nountrack;
  196.   ULONG  nosilent;
  197.   ULONG  nostrip;
  198.   ULONG  novirus;
  199.   ULONG  debug;
  200.   ULONG     quiet;
  201.   ULONG  saveall;
  202. };
  203.  
  204. struct CrunchMemList {
  205.   struct CrunchMemList * cml_Next;
  206.   APTR             cml_MemoryRegion;
  207.   ULONG             cml_MemorySize;
  208. };
  209.  
  210. #define CHECKXFLAG_SAVE            (1<< 0)
  211. #define CHECKXFLAG_ALL            (1<< 1)
  212. #define CHECKXFLAG_ASKPWD        (1<< 2)
  213. #define CHECKXFLAG_PRINTALL        (1<< 3)
  214. #define CHECKXFLAG_PRINTEXEC        (1<< 4)
  215. #define CHECKXFLAG_NODECRUNCH        (1<< 5)
  216. #define CHECKXFLAG_NOUNLINK        (1<< 6)
  217. #define CHECKXFLAG_NOUNARCHIVE        (1<< 7)
  218. #define CHECKXFLAG_NOUNTRACK        (1<< 8)
  219. #define CHECKXFLAG_NOSTRIP        (1<< 9)
  220. #define CHECKXFLAG_DEBUG        (1<<10)
  221. #define CHECKXFLAG_QUIET        (1<<11)
  222. #define CHECKXFLAG_SAVEALL        (1<<12)
  223.  
  224. #define CHECKXFLAG_XVSLIB        (1<<20)
  225.  
  226. #define CHKXCALLFLAGS    (CHECKXFLAG_SAVE|CHECKXFLAG_ALL|        \
  227.              CHECKXFLAG_ASKPWD|CHECKXFLAG_PRINTALL|        \
  228.              CHECKXFLAG_PRINTEXEC|CHECKXFLAG_NODECRUNCH|    \
  229.              CHECKXFLAG_NOUNLINK|CHECKXFLAG_NOUNARCHIVE|    \
  230.              CHECKXFLAG_NOUNTRACK|CHECKXFLAG_NOSTRIP|    \
  231.              CHECKXFLAG_DEBUG|CHECKXFLAG_QUIET|        \
  232.              CHECKXFLAG_XVSLIB|CHECKXFLAG_SAVEALL)
  233.  
  234. #define CHKXSAVEFLAGS    (CHECKXFLAG_LINKED|CHECKXFLAG_CRUNCHED|        \
  235.              CHECKXFLAG_STRIPPED|CHECKXFLAG_SAVEALL)
  236.  
  237. #define CHECKXFLAG_NAMEPRINTED        (1<<26)
  238. #define CHECKXFLAG_CRUNCHED        (1<<27)
  239. #define CHECKXFLAG_LINKED        (1<<28)
  240. #define CHECKXFLAG_ADDRESS        (1<<29)
  241. #define CHECKXFLAG_NOFREEMEM        (1<<30)
  242. #define CHECKXFLAG_STRIPPED        (1<<31)
  243.  
  244. #define CHXWARN_OFFSET        30
  245. #define XADERR_OFFSET        0x100
  246. #define XFDERR_OFFSET        0x200
  247.  
  248. #define CHKXERR_NOMEMORY    1
  249. #define CHKXERR_EXAMINEERR    2
  250. #define CHKXERR_OPENERR        3
  251. #define CHKXERR_READ        4
  252. #define CHKXERR_SCANERR        5
  253. #define CHKXERR_BREAK        6
  254. #define CHKXERR_OPENDIR        7
  255. #define CHKXERR_RESOURCE    8
  256. #define CHKXERR_NOBOOTVIRUS    9
  257. #define CHKXERR_WRITE        10
  258. #define CHKXERR_NOVIRUS        11
  259. #define CHKXERR_NOMEMORYARC    12
  260.  
  261. #define CHKXWARN_NOVIRUS    31
  262. #define CHKXWARN_XVSSELFTEST    32
  263. #define    CHKXWARN_MEMVIRUS    33
  264. #define CHKXWARN_EMPTY        34
  265.  
  266. struct FileData {
  267.   struct CrunchMemList * fd_MemList;
  268.   STRPTR         fd_Name;
  269.   BPTR           fd_LogFileFH;
  270.   BPTR             fd_ArcFileFH;
  271.   ULONG           fd_SaveDirL;
  272.   ULONG             fd_Flags;
  273.   ULONG             fd_NumVirus;
  274.   ULONG             fd_CHKXErrors;
  275.   ULONG             fd_XFDErrors;
  276.   ULONG             fd_XADErrors;
  277.   ULONG             fd_CorruptedArchives;
  278.   BYTE           fd_RecurseDepth;
  279.   UBYTE           fd_LinkNum;
  280.   UBYTE             fd_ArchiveDepth;
  281. };
  282.  
  283. static void SetLogComment(STRPTR);
  284. static LONG DoDirectoryScan(STRPTR, STRPTR, struct FileData *);
  285. static LONG DoFileOpen(struct FileData *);
  286. static LONG DoGetVirus(struct FileData *, APTR, ULONG);
  287. ASM(LONG) OutHookCheckX(REG(a0, struct Hook *hook), REG(a1, struct xadHookParam *hp));
  288. static LONG DoFileUnArchive(struct FileData *, APTR, ULONG);
  289. static LONG DoFileUnLink(struct FileData *, APTR, ULONG);
  290. static LONG DoFileUnCrunch(struct FileData *, APTR, ULONG);
  291. static LONG DoFileStrip(struct FileData *, APTR, ULONG);
  292. static void PrintCHKXFile(struct FileData *);
  293. static void PrintCHKXErr(struct FileData *, LONG);
  294. static void PrintCHKXTxt(struct FileData *, STRPTR, ...);
  295. static LONG AddCrunchMemList(struct FileData *, APTR, ULONG);
  296. static void FreeCrunchMemList(struct FileData *, APTR);
  297. static LONG SaveUncrFile(struct FileData *, APTR, ULONG);
  298. static ULONG OpenParentDir(struct FileData *);
  299. static ULONG OpenNewDir(struct FileData *, STRPTR);
  300.  
  301. /* All memory regions must be in mem list. All unneeded memory must be freed
  302.    as fast as possible (after unlinking, decrunching), as well as the
  303.    MemoryList structure.
  304.    
  305.    The program has a loop like scan routine system, which is called for
  306.    every file:
  307.  
  308.    A) Scan files, directories and sub directories and call following for
  309.       every file:
  310.    1) Check for viruses.
  311.    2) Test if it is an archive. When yes decrunch and start for every file
  312.       with point 1.
  313.    3) Test if file is linked. When yes unlink and call point 1 for both
  314.       parts.
  315.    4) Test if file is crunched. When yes, decrunch and start again with
  316.       point 1.
  317.    5) Try stripping useless stuff. When successful start with point 1 again.
  318.    6) Possibly save file (with SAVE option) or end loop here.
  319. */
  320.  
  321. /* main routine, do argument parsing */
  322. LONG start(void)
  323. {
  324.   LONG error = RETURN_FAIL;
  325.   struct DosLibrary *dosbase;
  326.   struct Process *task;
  327.  
  328.   SysBase = (*((struct ExecBase **) 4));
  329.  
  330.   /* test for WB and reply startup-message */
  331.   if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  332.   {
  333.     WaitPort(&task->pr_MsgPort);
  334.     Forbid();
  335.     ReplyMsg(GetMsg(&task->pr_MsgPort));
  336.     return RETURN_FAIL;
  337.   }
  338.  
  339.   if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  340.   {
  341.     struct xfdMasterBase *xfdmasterbase;
  342.  
  343.     DOSBase = dosbase;
  344.     if((xfdmasterbase = (struct xfdMasterBase *) OpenLibrary("xfdmaster.library", 39)))
  345.     {
  346.       struct xadMasterBase *xadmasterbase;
  347.  
  348.       xfdMasterBase = xfdmasterbase;
  349.       if((xadmasterbase = (struct xadMasterBase *) OpenLibrary("xadmaster.library", 10)))
  350.       {
  351.         struct Args Args;
  352.         struct RDArgs *rda;
  353.  
  354.     xadMasterBase = xadmasterbase;
  355.         memset(&Args, 0, sizeof(struct Args));
  356.  
  357.         if((rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
  358.         {
  359.           rda->RDA_ExtHelp =
  360.           "FROM        source file or directory - may contain patterns\n"
  361.           "LOG         log file name\n"
  362.           "SAVE        directory, where decrunched files are saved\n"
  363.           "ALL         scan deep into directories\n"
  364.           "ASKPWD      ask for password when needed (needs xpkmaster.library)\n"
  365.       "PRINTALL    print all filenames\n"
  366.           "PRINTEXEC   print names of all executable files\n"
  367.           "NODECRUNCH  do not decrunch files with xfdmaster\n"
  368.           "NOUNLINK    do not unlink files with xfdmaster\n"
  369.           "NOUNARCHIVE do not unarchive file archives\n"
  370.           "NOUNTRACK   do not unarchive track archives\n"
  371.           "NOSILENT    do not disable dos requests\n"
  372.           "NOSTRIP     do not strip useless hunks\n"
  373.           "NOVIRUS     do not scan with xvs.library for viruses\n"
  374.           "DEBUG       also output texts to serial debug engine\n"
  375.           "QUIET       do not output texts to console\n"
  376.           "SAVEALL     saves all files (also uncrunched) except address files\n";
  377.  
  378.           if(ReadArgs(PARAM, (LONG *) &Args, rda))
  379.           {
  380.             ULONG flags = 0, log = 0;
  381.             struct xvsBase *xvsbase = 0;
  382.  
  383.             if(!Args.from)        Args.from = "#?";
  384.             if(Args.all)        flags |= CHECKXFLAG_ALL;
  385.             if(Args.save)        flags |= CHECKXFLAG_SAVE;
  386.             if(Args.saveall)        flags |= CHECKXFLAG_SAVEALL;
  387.             if(Args.askpwd)        flags |= CHECKXFLAG_ASKPWD;
  388.         if(Args.printall)        flags |= CHECKXFLAG_PRINTALL;
  389.         if(Args.printexec)        flags |= CHECKXFLAG_PRINTEXEC;
  390.             if(Args.nodecrunch)        flags |= CHECKXFLAG_NODECRUNCH;
  391.             if(Args.nounlink)         flags |= CHECKXFLAG_NOUNLINK;
  392.             if(Args.nounarchive)    flags |= CHECKXFLAG_NOUNARCHIVE;
  393.             if(Args.nountrack)        flags |= CHECKXFLAG_NOUNTRACK;
  394.             if(Args.nostrip)        flags |= CHECKXFLAG_NOSTRIP;
  395.             if(Args.debug)        flags |= CHECKXFLAG_DEBUG;
  396.             if(Args.quiet)        flags |= CHECKXFLAG_QUIET;
  397.  
  398.             if(!Args.novirus)
  399.             {
  400.               if((xvsbase = (struct xvsBase *) OpenLibrary("xvs.library", 33)))
  401.               {
  402.                 flags |= CHECKXFLAG_XVSLIB;
  403.                 xvsBase = xvsbase;
  404.               }
  405.         }
  406.  
  407.             if(!Args.log || (log = Open(Args.log, MODE_READWRITE)))
  408.             {
  409.           struct FileData fd;
  410.           APTR win;
  411.           ULONG s1 = 0, s2 = 0, msecs;
  412.           struct IntuitionBase *intuitionbase;
  413.  
  414.           win = task->pr_WindowPtr;
  415.           if(!Args.nosilent)
  416.             task->pr_WindowPtr = (APTR) -1;
  417.           /* prevent dos requests */
  418.  
  419.           if(log)
  420.           {
  421.             SetFileSize(log, 0, OFFSET_BEGINNING);
  422.             SetProtection(Args.log, FIBF_EXECUTE);
  423.             SetLogComment(Args.log);
  424.           }
  425.  
  426.           memset(&fd, 0, sizeof(struct FileData));
  427.               fd.fd_Flags = flags | CHECKXFLAG_NAMEPRINTED; /* for possible warnings display */
  428.               fd.fd_LogFileFH = log;
  429.  
  430.           if(!xvsbase)
  431.             PrintCHKXErr(&fd, CHKXWARN_NOVIRUS);
  432.           else
  433.           {
  434.                 struct xvsMemoryInfo *mi;
  435.  
  436.             if(!xvsSelfTest())
  437.               PrintCHKXErr(&fd, CHKXWARN_XVSSELFTEST);
  438.  
  439.                   if((mi = (struct xvsMemoryInfo *) xvsAllocObject(XVSOBJ_MEMORYINFO)))
  440.                   { 
  441.                     if(xvsSurveyMemory(mi))
  442.                 PrintCHKXErr(&fd, CHKXWARN_MEMVIRUS);
  443.  
  444.                     xvsFreeObject(mi);
  445.                   }
  446.           }
  447.  
  448.           if((intuitionbase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)))
  449.           {
  450.             ASSIGN_INT
  451.             CurrentTime(&s1, &msecs);
  452.           }    
  453.  
  454.           ++fd.fd_RecurseDepth;
  455.           error = DoDirectoryScan(Args.from, Args.save, &fd) ? RETURN_FAIL : RETURN_OK;
  456.           --fd.fd_RecurseDepth;
  457.           fd.fd_Flags |= CHECKXFLAG_NAMEPRINTED; /* for result texts */
  458.  
  459.           task->pr_WindowPtr = win;
  460.  
  461.           if(intuitionbase)
  462.           {
  463.             CurrentTime(&s2, &msecs);
  464.             s2 -= s1;
  465.             s1 = s2 / 60;
  466.             s2 %= 60;
  467.             msecs = s1 / 60;
  468.             s1 %= 60;
  469.             PrintCHKXTxt(&fd, "\nTime needed for check: %2ld:%02ld:%02ld", msecs, s1, s2);
  470.             CloseLibrary((struct Library *) intuitionbase);
  471.           }
  472.           if(fd.fd_NumVirus)
  473.             PrintCHKXTxt(&fd, "The scan detected %ld virus%s.", fd.fd_NumVirus, fd.fd_NumVirus > 1 ? "es" : "");
  474.           if(fd.fd_CHKXErrors || fd.fd_XADErrors || fd.fd_XFDErrors || fd.fd_CorruptedArchives)
  475.             PrintCHKXTxt(&fd, "There were errors (CheckX/XFD/XAD/corrupted archives): %ld/%ld/%ld/%ld.",
  476.             fd.fd_CHKXErrors, fd.fd_XFDErrors, fd.fd_XADErrors, fd.fd_CorruptedArchives);
  477.           if(!error && (fd.fd_NumVirus || !xvsBase))
  478.           {
  479.             SetIoErr(0);
  480.             error = RETURN_WARN;
  481.           }
  482.  
  483.               if(log)
  484.                 Close(log);
  485.             }
  486.             else if(Args.log && !Args.quiet)
  487.               Printf("Could not create logfile.\n");
  488.  
  489.         if(xvsbase)
  490.           CloseLibrary((struct Library *) xvsbase);
  491.             FreeArgs(rda);
  492.           }
  493.           else
  494.             PrintFault(IoErr(), "CheckX");
  495.           FreeDosObject(DOS_RDARGS, rda);
  496.         }
  497.     CloseLibrary((struct Library *) xadmasterbase);
  498.       }
  499.       else
  500.         Printf("CheckX requires xadmaster.library version 10 or better.\n");
  501.       CloseLibrary((struct Library *) xfdmasterbase);
  502.     }
  503.     else
  504.       Printf("CheckX requires xfdmaster.library version 39 or better.\n");
  505.     CloseLibrary((struct Library *) dosbase);
  506.   }
  507.  
  508.   return error;
  509. }
  510.  
  511. static void SetLogComment(STRPTR name)
  512. {
  513.   UBYTE com[30];
  514.   struct MsgPort *TimerMP;
  515.  
  516.   if((TimerMP = CreateMsgPort()))
  517.   {
  518.     struct timerequest *TimerIO;
  519.  
  520.     if((TimerIO = (struct timerequest *) CreateIORequest(TimerMP,
  521.     sizeof(struct timerequest))))
  522.     {
  523.       if(!OpenDevice("timer.device",UNIT_VBLANK,
  524.       (struct IORequest *)TimerIO,0))
  525.       {
  526.         struct UtilityBase *utilitybase;
  527.  
  528.         TimerIO->tr_node.io_Command = TR_GETSYSTIME;
  529.         DoIO((struct IORequest *) TimerIO);
  530.         if((utilitybase = (struct UtilityBase *) OpenLibrary("utility.library",37)))
  531.         {
  532.           struct ClockData dat;
  533.  
  534.       ASSIGN_UTIL
  535.           Amiga2Date(TimerIO->tr_time.tv_secs, &dat);
  536.           sprintf(com, "CheckX " VERSION "." REVISION " - %02ld.%02ld.%ld", dat.mday, dat.month, dat.year);
  537.       CloseLibrary((struct Library *) utilitybase);
  538.       SetComment(name, com);
  539.         }
  540.         CloseDevice((struct IORequest *) TimerIO);
  541.       }
  542.       DeleteIORequest(TimerIO);
  543.     }
  544.     DeleteMsgPort(TimerMP);
  545.   }
  546. }
  547.  
  548. /* This scans a directory and calls DoFileOpen for every file. It
  549.    automatically creates SAVE destination directories when necessary. */
  550. static LONG DoDirectoryScan(STRPTR name, STRPTR sav, struct FileData *fd)
  551. {
  552.   struct AnchorPath *APath;
  553.   LONG error = CHKXERR_SCANERR;
  554.   ULONG retval;
  555.  
  556.   if(!(fd->fd_Flags & CHECKXFLAG_SAVE) || !sav ||
  557.   (fd->fd_SaveDirL = Lock(sav, SHARED_LOCK)))
  558.   {
  559.     if((APath = (struct AnchorPath *) AllocMem(sizeof(struct AnchorPath) +
  560.     512, MEMF_PUBLIC|MEMF_CLEAR)))
  561.     {
  562.       fd->fd_Name = APath->ap_Buf;
  563.       APath->ap_Strlen = 256;
  564.       for(retval = MatchFirst(name, APath); !retval;
  565.       retval = MatchNext(APath))
  566.       {
  567.         if(APath->ap_Flags & APF_DIDDIR)
  568.         {
  569.           OpenParentDir(fd);
  570.           APath->ap_Flags &= ~APF_DIDDIR; /* clear flag */
  571.         }
  572.         else if(APath->ap_Info.fib_DirEntryType > 0)
  573.         {
  574.           if(fd->fd_Flags & CHECKXFLAG_ALL)
  575.           {
  576.             OpenNewDir(fd, APath->ap_Info.fib_FileName);
  577.             APath->ap_Flags |= APF_DODIR;
  578.           }
  579.         }
  580.         else
  581.         {
  582.       fd->fd_Flags &= CHKXCALLFLAGS;
  583.           fd->fd_LinkNum = 0;
  584.           PrintCHKXErr(fd, DoFileOpen(fd));
  585.  
  586.           while(fd->fd_MemList)
  587.             FreeCrunchMemList(fd, fd->fd_MemList->cml_MemoryRegion);
  588.         }
  589.         if((fd->fd_Flags & CHECKXFLAG_SAVE) && !fd->fd_SaveDirL)
  590.         {
  591.           error = CHKXERR_OPENDIR; break;
  592.         }
  593.         if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
  594.         {
  595.           error = CHKXERR_BREAK; break;
  596.         }
  597.       }
  598.       MatchEnd(APath);
  599.  
  600.       if(retval == ERROR_NO_MORE_ENTRIES)
  601.         error = 0;
  602.  
  603.       FreeMem(APath, sizeof(struct AnchorPath) + 512);
  604.     }
  605.     else
  606.       error = CHKXERR_NOMEMORY;
  607.  
  608.     if(sav && fd->fd_SaveDirL)
  609.       UnLock(fd->fd_SaveDirL);
  610.   }
  611.   else
  612.     error = CHKXERR_OPENDIR;
  613.  
  614.   fd->fd_Flags |= CHECKXFLAG_NAMEPRINTED;
  615.   if(error)
  616.     PrintCHKXErr(fd, error);
  617.  
  618.   return error;
  619. }
  620.  
  621. /* Open a file and call DoGetVirus to scan */
  622. static LONG DoFileOpen(struct FileData *fd)
  623. {
  624.   struct FileInfoBlock *fib;
  625.   LONG ret = 0;
  626.  
  627.   if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  628.   {
  629.     ULONG fh;
  630.  
  631.     if((fh = Open(fd->fd_Name, MODE_OLDFILE)))
  632.     {
  633.       if(ExamineFH(fh, fib))
  634.       {
  635.         APTR mem;
  636.  
  637.     if(!fib->fib_Size)
  638.           ret = CHKXWARN_EMPTY;
  639.         else if((mem = AllocMem(fib->fib_Size, MEMF_ANY)))
  640.         {
  641.           if(Read(fh, mem, fib->fib_Size) != fib->fib_Size)
  642.             ret = CHKXERR_READ;
  643.  
  644.           if(!ret && !(ret = AddCrunchMemList(fd, mem, fib->fib_Size)))
  645.           {
  646.             if((fd->fd_Flags & CHECKXFLAG_PRINTALL) ||
  647.             ((fd->fd_Flags & CHECKXFLAG_PRINTEXEC) && fib->fib_Size > 4
  648.             && *((ULONG *)mem) == HUNK_HEADER))
  649.               PrintCHKXFile(fd);
  650.             ret = DoGetVirus(fd, mem, fib->fib_Size);
  651.           }
  652.           else
  653.             FreeMem(mem, fib->fib_Size);
  654.         }
  655.         else
  656.         {
  657.           fd->fd_ArcFileFH = fh;
  658.           PrintCHKXErr(fd, CHKXERR_NOMEMORYARC);
  659.       ret = DoFileUnArchive(fd, 0, fib->fib_Size);
  660.         }
  661.       }
  662.       else
  663.         ret = CHKXERR_EXAMINEERR;
  664.       Close(fh);
  665.     }
  666.     else
  667.       ret = CHKXERR_OPENERR;
  668.   
  669.     FreeDosObject(DOS_FIB, fib);
  670.   }
  671.   else
  672.     ret = CHKXERR_NOMEMORY;
  673.  
  674.   return ret;
  675. }
  676.  
  677. /* for SAVE option: open parent directory and try to delete the directory
  678.    we leave. This only succeeds, when the directory is empty. */
  679. static ULONG OpenParentDir(struct FileData *fd)
  680. {
  681.   ULONG g;
  682.   UBYTE name[300];
  683.  
  684.   if((g = fd->fd_SaveDirL))
  685.   {
  686.     NameFromLock(g, name, 300);
  687.     fd->fd_SaveDirL = ParentDir(g);
  688.     UnLock(g);
  689.  
  690.     DeleteFile(name);
  691.   }
  692.   else
  693.     return -1;
  694.  
  695.   return fd->fd_SaveDirL;
  696. }
  697.  
  698. /* Open a new subdirectory for SAVE option */
  699. static ULONG OpenNewDir(struct FileData *fd, STRPTR name)
  700. {
  701.   ULONG g;
  702.  
  703.   if(fd->fd_SaveDirL)
  704.   {
  705.     g = CurrentDir(fd->fd_SaveDirL);
  706.     if(!(fd->fd_SaveDirL = Lock(name, SHARED_LOCK)))
  707.       if((fd->fd_SaveDirL = CreateDir(name)))
  708.         ChangeMode(CHANGE_LOCK, fd->fd_SaveDirL, SHARED_LOCK);
  709.     UnLock(CurrentDir(g));
  710.   }
  711.   else
  712.     return -1;
  713.  
  714.   return fd->fd_SaveDirL;
  715. }
  716.  
  717. static LONG DoGetVirus(struct FileData *fd, APTR buffer, ULONG buflength)
  718. {
  719.   if(!buflength) /* restarted with empty file */
  720.     return CHKXWARN_EMPTY;
  721.  
  722.   if(fd->fd_Flags & CHECKXFLAG_XVSLIB)
  723.   {
  724.     APTR mem;
  725.       
  726.     if((mem = AllocMem(buflength, MEMF_ANY)))
  727.     {
  728.       struct xvsFileInfo *fi;
  729.       if((fi = (struct xvsFileInfo *) xvsAllocObject(XVSOBJ_FILEINFO)))
  730.       {
  731.         ULONG i;
  732.     /* xvs may modify the buffer! */
  733.         CopyMem(buffer, mem, buflength);
  734.         fi->xvsfi_File = mem;
  735.         fi->xvsfi_FileLen = buflength;
  736.         i = xvsCheckFile(fi);
  737.         if(i == XVSFT_DATAVIRUS)
  738.         {
  739.        PrintCHKXTxt(fd, "Data-Virus '%s'", fi->xvsfi_Name);
  740.        ++fd->fd_NumVirus;
  741.      }
  742.         else if(i == XVSFT_FILEVIRUS)
  743.         {
  744.        PrintCHKXTxt(fd, "File-Virus '%s'", fi->xvsfi_Name);
  745.        ++fd->fd_NumVirus;
  746.      }
  747.         else if(i == XVSFT_LINKVIRUS)
  748.         {
  749.        PrintCHKXTxt(fd, "Link-Virus '%s'", fi->xvsfi_Name);
  750.        ++fd->fd_NumVirus;
  751.      }
  752.         xvsFreeObject(fi);
  753.       }
  754.       else
  755.         PrintCHKXErr(fd, CHKXERR_NOVIRUS);
  756.       FreeMem(mem, buflength);
  757.     }
  758.     else
  759.       PrintCHKXErr(fd, CHKXERR_NOVIRUS);
  760.   }
  761.  
  762.   return DoFileUnArchive(fd, buffer, buflength);
  763. }
  764.  
  765. /* For bootblocks */
  766. ASM(LONG) OutHookCheckX(REG(a0, struct Hook *hook), REG(a1, struct xadHookParam *hp))
  767. {
  768.   ULONG i;
  769.  
  770.   switch(hp->xhp_Command)
  771.   {
  772.   case XADHC_WRITE:
  773.     if((i = 1024-hp->xhp_DataPos) > hp->xhp_BufferSize)
  774.       i = hp->xhp_BufferSize;
  775.     CopyMem(hp->xhp_BufferPtr, ((STRPTR)(hook->h_Data))+hp->xhp_DataPos, i);
  776.     hp->xhp_DataPos += i;
  777.     if(hp->xhp_DataPos == 1024)
  778.       return 10000; /* CheckX own error code for error detection */
  779.   case XADHC_INIT:
  780.   case XADHC_FREE:
  781.   case XADHC_ABORT:
  782.     break;
  783.   default: return XADERR_NOTSUPPORTED;
  784.   }
  785.  
  786.   return 0;
  787. }
  788.  
  789. ASM(LONG) BreakHookCheckX(void)
  790. {
  791.   return (SetSignal(0L,0L) & SIGBREAKF_CTRL_C) ? 0 : XADPIF_OK;
  792. }
  793. struct Hook breakhook = {{0,0},(ULONG (*)()) BreakHookCheckX, 0};
  794.  
  795. /* To get real file size */
  796. ASM(LONG) OutHookCheckXSize(REG(a0, struct Hook *hook), REG(a1, struct xadHookParam *hp))
  797. {
  798.   switch(hp->xhp_Command)
  799.   {
  800.   case XADHC_WRITE:
  801.     hp->xhp_DataPos += hp->xhp_BufferSize;
  802.     hook->h_Data = (APTR) hp->xhp_DataPos;
  803.   case XADHC_INIT:
  804.   case XADHC_FREE:
  805.   case XADHC_ABORT:
  806.     break;
  807.   default: return XADERR_NOTSUPPORTED;
  808.   }
  809.  
  810.   return 0;
  811. }
  812.  
  813. static void CheckArcFiles(struct FileData *fd, struct xadArchiveInfo *ai, ULONG disk)
  814. {
  815.   struct xadFileInfo *fi;
  816.   struct TagItem ti[6];
  817.   LONG numloop = 0;
  818.   STRPTR buf = 0;
  819.  
  820.   ti[0].ti_Tag = XAD_OUTMEMORY;
  821.   ti[1].ti_Tag = XAD_OUTSIZE;
  822.   ti[2].ti_Tag = XAD_ENTRYNUMBER;
  823.   ti[3].ti_Tag = XAD_PROGRESSHOOK;
  824.   ti[3].ti_Data = (ULONG) &breakhook;
  825.   ti[4].ti_Tag = TAG_DONE;
  826.  
  827.   fi = ai->xai_FileInfo;
  828.   while(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && fi)
  829.   {
  830.     APTR dest;
  831.  
  832.     if(!(fi->xfi_Flags & (XADFIF_DIRECTORY|XADFIF_LINK)))
  833.     {
  834.       LONG err = 0;
  835.       ULONG flags, size, linknum;
  836.  
  837.       flags = fd->fd_Flags;
  838.       linknum = fd->fd_LinkNum;
  839.       fd->fd_LinkNum = 0;
  840.       if(!(fi->xfi_Flags & XADFIF_NOFILENAME))
  841.         fd->fd_Flags &= ~CHECKXFLAG_NAMEPRINTED;
  842.       fd->fd_Name = fi->xfi_FileName;
  843.       fd->fd_RecurseDepth++;
  844.       fd->fd_ArchiveDepth++;
  845.  
  846.       size = fi->xfi_Size;
  847.       if(fi->xfi_Flags & XADFIF_NOUNCRUNCHSIZE)
  848.       {
  849.     struct Hook hook = {{0,0},(ULONG (*)()) OutHookCheckXSize, 0, 0};
  850.       struct TagItem ti[5];
  851.  
  852.     ti[0].ti_Tag = XAD_OUTHOOK;
  853.     ti[0].ti_Data = (ULONG) &hook;
  854.       ti[1].ti_Tag = XAD_ENTRYNUMBER;
  855.     ti[1].ti_Data = fi->xfi_EntryNumber;
  856.         ti[2].ti_Tag = XAD_PROGRESSHOOK;
  857.         ti[2].ti_Data = (ULONG) &breakhook;
  858.       ti[3].ti_Tag = buf ? XAD_PASSWORD : TAG_IGNORE;
  859.       ti[3].ti_Data = (ULONG) buf;
  860.         ti[4].ti_Tag = TAG_DONE;
  861.  
  862.         if(!(err = (disk ? xadDiskFileUnArcA(ai, ti) : xadFileUnArcA(ai, ti))))
  863.       size = (ULONG) hook.h_Data;
  864.       }
  865.  
  866.       if(!size)
  867.       {
  868.         if(!err)
  869.           err = CHKXWARN_EMPTY;
  870.       }
  871.       else if((dest = AllocMem(size, MEMF_PUBLIC)))
  872.       {
  873.         if(!(err = AddCrunchMemList(fd, dest, size)))
  874.         {
  875.       ti[0].ti_Data = (ULONG) dest;
  876.       ti[1].ti_Data = size;
  877.       ti[2].ti_Data = fi->xfi_EntryNumber;
  878.           ti[4].ti_Tag = buf ? XAD_PASSWORD : TAG_IGNORE;
  879.       ti[4].ti_Data = (ULONG) buf;
  880.  
  881.           if((err = disk ? xadDiskFileUnArcA(ai, ti) : xadFileUnArcA(ai, ti)))
  882.       {
  883.         if(disk)
  884.           err = CHKXERR_READ;
  885.         else
  886.               err += XADERR_OFFSET;
  887.           }
  888.           else
  889.           {
  890.             if((fd->fd_Flags & CHECKXFLAG_PRINTALL) ||
  891.         ((fd->fd_Flags & CHECKXFLAG_PRINTEXEC) && size > 4
  892.         && *((ULONG *)dest) == HUNK_HEADER))
  893.               PrintCHKXFile(fd);
  894.             err = DoGetVirus(fd, dest, size);
  895.           }
  896.           FreeCrunchMemList(fd, dest);
  897.         }
  898.         else
  899.           FreeMem(dest, size);
  900.       }
  901.       else
  902.         err = CHKXERR_NOMEMORY;
  903.  
  904.       if((err == XADERR_OFFSET + XADERR_PASSWORD) && !numloop)
  905.       {
  906.         struct Library *xpkbase;
  907.         if(fd->fd_Flags & CHECKXFLAG_ASKPWD && (xpkbase = OpenLibrary(XPKNAME, 4)))
  908.         {
  909.           if(buf)
  910.             FreeMem(buf, 256);
  911.           if((buf = (STRPTR) AllocMem(256, MEMF_ANY|MEMF_CLEAR)))
  912.       {
  913.             if(XpkPassRequestTags(XPK_PasswordBuf, buf, XPK_PassBufSize, 256,
  914.             XPK_PassTitle, fd->fd_Name, TAG_DONE))
  915.             {
  916.               FreeMem(buf, 256); buf = 0; fi = fi->xfi_Next;
  917.             }
  918.             else
  919.             {
  920.               numloop = -1; err = 0;
  921.             }
  922.           }
  923.  
  924.           CloseLibrary(xpkbase);
  925.         }
  926.       }
  927.  
  928.       if(err)
  929.       {
  930.         if(fi->xfi_Flags & XADFIF_NOFILENAME)
  931.           fd->fd_Flags &= ~CHECKXFLAG_NAMEPRINTED;
  932.         PrintCHKXErr(fd, err);
  933.       }
  934.  
  935.       --fd->fd_RecurseDepth;
  936.       --fd->fd_ArchiveDepth;
  937.       fd->fd_Flags = flags;
  938.       fd->fd_LinkNum = linknum;
  939.     }
  940.     if(++numloop)
  941.     {
  942.       fi = fi->xfi_Next;
  943.       numloop = 0;
  944.     }
  945.     else
  946.       ++numloop;
  947.   }
  948.   if(buf)
  949.     FreeMem(buf, 256);
  950. }
  951.  
  952. /* Tests if a file is an archive. When yes, the archive is unarchived and
  953.    the result files are scanned. Else DoFileUnLink is called.
  954. */
  955. static LONG DoFileUnArchive(struct FileData *fd, APTR buffer, ULONG buflength)
  956. {
  957.   LONG err = 0;
  958.   struct TagItem tih[2];
  959.   struct xadArchiveInfo *ai;
  960.  
  961.   if(buffer) { tih[0].ti_Tag = XAD_INMEMORY; tih[0].ti_Data = (ULONG) buffer; }
  962.   else { tih[0].ti_Tag = XAD_INFILEHANDLE; tih[0].ti_Data = (ULONG) fd->fd_ArcFileFH; }
  963.   tih[1].ti_Tag = TAG_DONE;
  964.  
  965.   if((ai = (struct xadArchiveInfo *) xadAllocObject(XADOBJ_ARCHIVEINFO, 0)))
  966.   {
  967.     STRPTR arcname;
  968.  
  969.     arcname = fd->fd_Name;
  970.     if(!xadGetInfo(ai, XAD_INSIZE, buflength, XAD_PROGRESSHOOK, &breakhook, TAG_MORE, tih))
  971.     {
  972.       if(ai->xai_Flags & XADAIF_FILECORRUPT)
  973.       {
  974.         ++fd->fd_CorruptedArchives;
  975.         PrintCHKXTxt(fd, "%s (ARCHIVE, CORRUPTED)", ai->xai_Client->xc_ArchiverName);
  976.       }
  977.       else
  978.         PrintCHKXTxt(fd, "%s (ARCHIVE)", ai->xai_Client->xc_ArchiverName);
  979.         
  980.       if(!(!(fd->fd_Flags & CHECKXFLAG_NOUNARCHIVE) && ai->xai_FileInfo ||
  981.       !(fd->fd_Flags & CHECKXFLAG_NOUNTRACK) && ai->xai_DiskInfo))
  982.       {
  983.         if(buffer)
  984.           err = DoFileUnLink(fd, buffer, buflength);
  985.       }
  986.       else if(!OpenNewDir(fd, FilePart(fd->fd_Name)))
  987.         err = CHKXERR_OPENERR;
  988.       else
  989.       {
  990.     if(!(fd->fd_Flags & CHECKXFLAG_NOUNARCHIVE) && ai->xai_FileInfo)
  991.       CheckArcFiles(fd, ai, 0);
  992.     if(!(fd->fd_Flags & CHECKXFLAG_NOUNTRACK) && ai->xai_DiskInfo)
  993.     {
  994.       STRPTR buf;
  995.  
  996.       /* reduce stacksize, as we have a recursive program */
  997.       if((buf = (STRPTR) AllocMem(1024, MEMF_PUBLIC)))
  998.       {
  999.             ULONG flags;
  1000.         struct xadDiskInfo *di;
  1001.         struct xadArchiveInfo *aid;
  1002.         di = ai->xai_DiskInfo;
  1003.  
  1004.             flags = fd->fd_Flags;
  1005.         while(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && di)
  1006.         {
  1007.           if(di->xdi_SectorSize != 512 || (di->xdi_TrackSectors != 11 &&
  1008.           di->xdi_TrackSectors != 22) || (!(di->xdi_Flags & XADDIF_NOHEADS) &&
  1009.           di->xdi_Heads != 2) || (!(di->xdi_Flags & XADDIF_NOCYLINDERS) &&
  1010.           di->xdi_Cylinders != 80))
  1011.             sprintf(buf, "-disk image %ld", di->xdi_EntryNumber);
  1012.           else
  1013.           {
  1014.             if(!(di->xdi_Flags & (XADDIF_NOHIGHCYL|XADDIF_NOLOWCYL)) &&
  1015.          (di->xdi_LowCyl || di->xdi_HighCyl != 79))
  1016.                   sprintf(buf, "-disk image %ld (%s, %ld to %ld)",
  1017.                   di->xdi_EntryNumber, di->xdi_TrackSectors == 22 ?
  1018.                   "HD" : "DD", di->xdi_LowCyl, di->xdi_HighCyl);
  1019.                 else
  1020.                   sprintf(buf, "-disk image %ld (%s)",
  1021.                   di->xdi_EntryNumber, di->xdi_TrackSectors == 22 ? "HD" : "DD");
  1022.               }
  1023.       
  1024.               fd->fd_Flags &= ~CHECKXFLAG_NAMEPRINTED;
  1025.           fd->fd_Name = buf;
  1026.               PrintCHKXFile(fd);
  1027.  
  1028.           if(di->xdi_TextInfo)
  1029.           {
  1030.                 struct xadTextInfo *ti;
  1031.                 ULONG flags, i = 1, linknum;
  1032.  
  1033.             for(ti = di->xdi_TextInfo; ti; ti = ti->xti_Next)
  1034.             {
  1035.               if(ti->xti_Size && ti->xti_Text)
  1036.               {
  1037.                     flags = fd->fd_Flags;
  1038.                     fd->fd_Flags &= ~CHECKXFLAG_NAMEPRINTED;
  1039.                     fd->fd_Flags |= CHECKXFLAG_NOFREEMEM;
  1040.                     fd->fd_RecurseDepth++;
  1041.                     linknum = fd->fd_LinkNum;
  1042.                     fd->fd_LinkNum = 0;
  1043.                     sprintf(buf, "--infotext %ld (size %ld)", i, ti->xti_Size);
  1044.                     if((fd->fd_Flags & CHECKXFLAG_PRINTALL) ||
  1045.                     ((fd->fd_Flags & CHECKXFLAG_PRINTEXEC) && ti->xti_Size > 4
  1046.                     && *((ULONG *)(ti->xti_Text)) == HUNK_HEADER))
  1047.                       PrintCHKXFile(fd);
  1048.                     PrintCHKXErr(fd, DoGetVirus(fd, ti->xti_Text, ti->xti_Size));
  1049.                     --fd->fd_RecurseDepth;
  1050.                     fd->fd_Flags = flags;
  1051.                     fd->fd_LinkNum = linknum;
  1052.                   }
  1053.                   ++i;
  1054.                 }
  1055.               }
  1056.  
  1057.           /* check bootblock here - special outhook for bootblock extraction */
  1058.           if(!di->xdi_LowCyl) /* boot check only, when first block! */
  1059.           {
  1060.             struct Hook hook = {{0,0},(ULONG (*)()) OutHookCheckX, 0};
  1061.  
  1062.             hook.h_Data = buf;
  1063.             if(xadDiskUnArc(ai, XAD_ENTRYNUMBER, di->xdi_EntryNumber,
  1064.             XAD_OUTHOOK, &hook, XAD_PROGRESSHOOK, &breakhook,
  1065.             TAG_DONE) == 10000) /* special hook return code! */
  1066.             {
  1067.               struct xvsBootInfo *bi;
  1068.  
  1069.               if((bi = (struct xvsBootInfo *) xvsAllocObject(XVSOBJ_BOOTINFO)))
  1070.                 {
  1071.                 bi->xvsbi_Bootblock = buf;
  1072.                 if(xvsCheckBootblock(bi) == XVSBT_VIRUS)
  1073.                 {
  1074.                   PrintCHKXTxt(fd, "Boot-Virus '%s'", bi->xvsbi_Name);
  1075.                   ++fd->fd_NumVirus;
  1076.                 }
  1077.                 xvsFreeObject(bi);
  1078.                 }
  1079.                 else
  1080.                   PrintCHKXErr(fd, CHKXERR_NOBOOTVIRUS);
  1081.               }
  1082.               else
  1083.                 PrintCHKXErr(fd, CHKXERR_NOBOOTVIRUS);
  1084.           }
  1085.     
  1086.           if((aid = (struct xadArchiveInfo *) xadAllocObject(XADOBJ_ARCHIVEINFO, 0)))
  1087.           {
  1088.             struct TagItem ti[4];
  1089.             struct xadClient *cl;
  1090.  
  1091.           ti[0].ti_Tag = XAD_INSIZE;
  1092.           ti[0].ti_Data = buflength;
  1093.           ti[1].ti_Tag = XAD_ENTRYNUMBER;
  1094.           ti[1].ti_Data = di->xdi_EntryNumber;
  1095.                 ti[2].ti_Tag = XAD_PROGRESSHOOK;
  1096.                 ti[2].ti_Data = (ULONG) &breakhook;
  1097.           ti[3].ti_Tag = TAG_MORE;
  1098.           ti[3].ti_Data = (ULONG) tih;
  1099.     
  1100.           if(!(err = xadGetDiskInfo(aid, XAD_NOEMPTYERROR, 1, XAD_PROGRESSHOOK, &breakhook,
  1101.           XAD_INDISKARCHIVE, ti, TAG_DONE)))
  1102.           {
  1103.             if(aid->xai_Flags & XADAIF_FILECORRUPT)
  1104.             {
  1105.                     ++fd->fd_CorruptedArchives;
  1106.                     PrintCHKXTxt(fd, "%s (FILESYSTEM, CORRUPTED)", aid->xai_Client->xc_ArchiverName);
  1107.             }
  1108.                   else
  1109.                     PrintCHKXTxt(fd, "%s (FILESYSTEM)", aid->xai_Client->xc_ArchiverName);
  1110.           CheckArcFiles(fd, aid, 1);
  1111.           cl = aid->xai_Client->xc_Next;
  1112.           xadFreeInfo(aid);
  1113.           while(cl) /* multiple filesystems */
  1114.           {
  1115.               if(!xadGetDiskInfo(aid, XAD_NOEMPTYERROR, 1, XAD_INDISKARCHIVE, ti,
  1116.               XAD_PROGRESSHOOK, &breakhook, XAD_STARTCLIENT, cl, TAG_MORE, TAG_DONE))
  1117.               {
  1118.                 if(aid->xai_Flags & XADAIF_FILECORRUPT)
  1119.                 {
  1120.                         ++fd->fd_CorruptedArchives;
  1121.                         PrintCHKXTxt(fd, "%s (FILESYSTEM, CORRUPTED)", aid->xai_Client->xc_ArchiverName);
  1122.                 }
  1123.                       else
  1124.                         PrintCHKXTxt(fd, "%s (FILESYSTEM)", aid->xai_Client->xc_ArchiverName);
  1125.               CheckArcFiles(fd, aid, 1);
  1126.               cl = aid->xai_Client->xc_Next;
  1127.               xadFreeInfo(aid);
  1128.             }
  1129.             else
  1130.               cl = 0;
  1131.           }
  1132.           }
  1133.           else
  1134.           {
  1135.             if(err != XADERR_FILESYSTEM)
  1136.               PrintCHKXErr(fd, XADERR_OFFSET + err);
  1137.             err = 0;
  1138.           }
  1139.           xadFreeObjectA(aid, 0);
  1140.           }
  1141.  
  1142.               di = di->xdi_Next;
  1143.             } /* while */
  1144.             fd->fd_Flags = flags;
  1145.             FreeMem(buf, 1024);
  1146.           } /* AllocMem buffer */
  1147.           else
  1148.             err = CHKXERR_NOMEMORY;
  1149.         } /* is there a disk entry? */
  1150.         if(!OpenParentDir(fd))
  1151.           err = CHKXERR_OPENDIR;
  1152.       }
  1153.  
  1154.       xadFreeInfo(ai);
  1155.     } /* xadGetInfo */
  1156.     else if(!xadGetDiskInfo(ai, XAD_NOEMPTYERROR, 1, XAD_PROGRESSHOOK, &breakhook,
  1157.     XAD_INSIZE, buflength, TAG_MORE, tih))
  1158.     {
  1159.       struct xadClient *cl;
  1160.       ULONG isvalid = 1;
  1161.  
  1162.       if(ai->xai_Flags & XADAIF_FILECORRUPT)
  1163.       {
  1164.         ++fd->fd_CorruptedArchives;
  1165.         PrintCHKXTxt(fd, "%s (FILESYSTEM, CORRUPTED)", ai->xai_Client->xc_ArchiverName);
  1166.       }
  1167.       else
  1168.         PrintCHKXTxt(fd, "%s (FILESYSTEM)", ai->xai_Client->xc_ArchiverName);
  1169.  
  1170.       if(fd->fd_Flags & CHECKXFLAG_NOUNTRACK)
  1171.       {
  1172.         if(buffer)
  1173.           err = DoFileUnLink(fd, buffer, buflength);
  1174.       }
  1175.       else if(!OpenNewDir(fd, FilePart(fd->fd_Name)))
  1176.         err = CHKXERR_OPENERR;
  1177.       else
  1178.       {
  1179.         CheckArcFiles(fd, ai, 1);
  1180.         while(isvalid && (cl = ai->xai_Client->xc_Next))
  1181.         {
  1182.           xadFreeInfo(ai);
  1183.           if(!xadGetDiskInfo(ai, XAD_NOEMPTYERROR, 1, XAD_PROGRESSHOOK, &breakhook,
  1184.           XAD_INSIZE, buflength, XAD_STARTCLIENT, cl, TAG_MORE, tih))
  1185.           {
  1186.             if(ai->xai_Flags & XADAIF_FILECORRUPT)
  1187.             {
  1188.               ++fd->fd_CorruptedArchives;
  1189.               PrintCHKXTxt(fd, "%s (FILESYSTEM, CORRUPTED)", ai->xai_Client->xc_ArchiverName);
  1190.             }
  1191.             else
  1192.               PrintCHKXTxt(fd, "%s (FILESYSTEM)", ai->xai_Client->xc_ArchiverName);
  1193.             CheckArcFiles(fd, ai, 1);
  1194.           }
  1195.           else
  1196.             isvalid = 0;
  1197.         } 
  1198.         if(!OpenParentDir(fd))
  1199.           err = CHKXERR_OPENDIR;
  1200.       }
  1201.       if(isvalid)
  1202.         xadFreeInfo(ai);
  1203.     }
  1204.     else if(buffer)
  1205.       err = DoFileUnLink(fd, buffer, buflength);
  1206.  
  1207.     fd->fd_Name = arcname;
  1208.     xadFreeObjectA(ai, 0);
  1209.   }
  1210.   else
  1211.     err = CHKXERR_NOMEMORY;
  1212.  
  1213.   return err;
  1214. }
  1215.  
  1216. /* Tries to unlink a file. When the file was linked, we call DoGetVirus
  1217.    for the two parts to check if they may be archives, else we call
  1218.    DoFileUnCrunch.
  1219. */
  1220. static LONG DoFileUnLink(struct FileData *fd, APTR buffer, ULONG buflength)
  1221. {
  1222.   LONG ret = CHKXERR_NOMEMORY;
  1223.   struct xfdLinkerInfo *xli;
  1224.  
  1225.   if((xli = (struct xfdLinkerInfo *) xfdAllocObject(XFDOBJ_LINKERINFO)))
  1226.   {
  1227.     xli->xfdli_Buffer = buffer;
  1228.     xli->xfdli_BufLen = buflength;
  1229.     if(xfdRecogLinker(xli))
  1230.     {
  1231.       PrintCHKXTxt(fd, xli->xfdli_LinkerName);
  1232.       if(fd->fd_Flags & CHECKXFLAG_NOUNLINK)
  1233.         ret = DoFileUnCrunch(fd, buffer, buflength);
  1234.       else if(xfdUnlink(xli))
  1235.       {
  1236.         ULONG flags;
  1237.     fd->fd_Flags |= CHECKXFLAG_LINKED;
  1238.         ++fd->fd_RecurseDepth;
  1239.         ++fd->fd_LinkNum;
  1240.     flags = fd->fd_Flags;
  1241.     fd->fd_Flags |= CHECKXFLAG_NOFREEMEM;
  1242.         PrintCHKXErr(fd, DoGetVirus(fd, xli->xfdli_Save1,
  1243.         xli->xfdli_SaveLen1));
  1244.         fd->fd_Flags = flags; /* CHECKXFLAG_NOFREEMEM is cleared */
  1245.         PrintCHKXErr(fd, DoGetVirus(fd, xli->xfdli_Save2,
  1246.         xli->xfdli_SaveLen2));
  1247.         ret = 0;
  1248.         --fd->fd_RecurseDepth;
  1249.       }
  1250.       else
  1251.         ret = XFDERR_OFFSET + xli->xfdli_Error;
  1252.     }
  1253.     else
  1254.       ret = DoFileUnCrunch(fd, buffer, buflength);
  1255.  
  1256.     xfdFreeObject(xli);
  1257.   }
  1258.   return ret;
  1259. }
  1260.  
  1261. /* Tries to decrunch a file. When it is crunched, we decrunch it and call
  1262.    DoGetVirus to start the loop again. Else we call unstripping.
  1263. */
  1264. static LONG DoFileUnCrunch(struct FileData *fd, APTR buffer, ULONG buflength)
  1265. {
  1266.   LONG ret = CHKXERR_NOMEMORY;
  1267.   struct xfdBufferInfo *xbi;
  1268.  
  1269.   if((xbi = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO)))
  1270.   {
  1271.     xbi->xfdbi_SourceBuffer = buffer;
  1272.     xbi->xfdbi_SourceBufLen = buflength;
  1273.     xbi->xfdbi_Flags = XFDFF_RECOGEXTERN;
  1274.     if(xfdRecogBuffer(xbi))
  1275.     {
  1276.       struct Library *xpkbase;
  1277.       STRPTR buf = 0;
  1278.       ULONG buflen = 0;
  1279.  
  1280.       PrintCHKXTxt(fd, xbi->xfdbi_PackerFlags & XFDPFF_ADDR ? "%s (ADDRESS)" :
  1281.       "%s", xbi->xfdbi_PackerName);
  1282.  
  1283.       if(fd->fd_Flags & CHECKXFLAG_ASKPWD && (xpkbase =
  1284.       OpenLibrary(XPKNAME, 4)))
  1285.       {
  1286.     ASSIGN_XPK
  1287.         if(xbi->xfdbi_PackerFlags & XFDPFF_PASSWORD)
  1288.         {
  1289.           buflen = (xbi->xfdbi_MaxSpecialLen == 0xFFFF) ? 256 :
  1290.         xbi->xfdbi_MaxSpecialLen;
  1291.           if((buf = (STRPTR) AllocMem(buflen, MEMF_ANY|MEMF_CLEAR)))
  1292.       {
  1293.             if(!XpkPassRequestTags(XPK_PasswordBuf, buf, XPK_PassTitle, fd->fd_Name, 
  1294.             XPK_PassBufSize, buflen, TAG_DONE))
  1295.           xbi->xfdbi_Special = buf;
  1296.       }
  1297.         }
  1298.         else if(xbi->xfdbi_PackerFlags & XFDPFF_KEY16)
  1299.         {
  1300.       if(!XpkPassRequestTags(XPK_Key16BitPtr, &buflen, XPK_PassTitle, fd->fd_Name, TAG_DONE))
  1301.         xbi->xfdbi_Special = &buflen;
  1302.         }
  1303.         else if(xbi->xfdbi_PackerFlags & XFDPFF_KEY32)
  1304.         {
  1305.       if(!XpkPassRequestTags(XPK_Key32BitPtr, &buflen, XPK_PassTitle, fd->fd_Name, TAG_DONE))
  1306.             xbi->xfdbi_Special = &buflen;
  1307.         }
  1308.     CloseLibrary(xpkbase);
  1309.       }
  1310.       if(fd->fd_Flags & CHECKXFLAG_NODECRUNCH)
  1311.         ret = DoFileStrip(fd, buffer, buflength);
  1312.       else if(xfdDecrunchBuffer(xbi))
  1313.       {
  1314.         if((xbi->xfdbi_PackerFlags & XFDPFF_ADDR) &&
  1315.         !(fd->fd_Flags & CHECKXFLAG_ADDRESS))
  1316.         {
  1317.       fd->fd_Flags |= CHECKXFLAG_ADDRESS;
  1318.           PrintCHKXErr(fd, SaveUncrFile(fd, buffer, buflength));
  1319.         }
  1320.     fd->fd_Flags |= CHECKXFLAG_CRUNCHED;
  1321.         FreeCrunchMemList(fd, buffer);
  1322.         ++fd->fd_RecurseDepth;
  1323.         if(!(ret = AddCrunchMemList(fd, xbi->xfdbi_TargetBuffer,
  1324.         xbi->xfdbi_TargetBufLen)))
  1325.         {
  1326.           PrintCHKXErr(fd, DoGetVirus(fd, xbi->xfdbi_TargetBuffer,
  1327.             xbi->xfdbi_TargetBufSaveLen));
  1328.         }
  1329.         else
  1330.           FreeMem(xbi->xfdbi_TargetBuffer, xbi->xfdbi_TargetBufLen);
  1331.         --fd->fd_RecurseDepth;
  1332.       }
  1333.       else
  1334.         ret = XFDERR_OFFSET + xbi->xfdbi_Error;
  1335.  
  1336.       if(buf)
  1337.         FreeMem(buf, buflen);
  1338.     }
  1339.     else
  1340.       ret = DoFileStrip(fd, buffer, buflength);
  1341.  
  1342.     xfdFreeObject(xbi);
  1343.   }
  1344.   return ret;
  1345. }
  1346.  
  1347. /* Tries to strip useless hunks in a file. When there are some, we remove
  1348.    them and call DoGetVirus to start the loop again. Else we finish.
  1349.    When either unlinking or uncrunching happend in before loops, we may
  1350.    save the file when there was SAVE option.
  1351. */
  1352. static LONG DoFileStrip(struct FileData *fd, APTR buffer, ULONG buflength)
  1353. {
  1354.   LONG ret = 0;
  1355.   ULONG reslength = buflength;
  1356.  
  1357.   if(*((ULONG *) buffer) == 0x000003F3 && !(fd->fd_Flags &
  1358.   CHECKXFLAG_NOSTRIP))
  1359.     xfdStripHunks(buffer, buflength, &reslength,
  1360.     XFDSHF_NAME|XFDSHF_SYMBOL|XFDSHF_DEBUG);
  1361.     /* errors are not interpreted */
  1362.  
  1363.   if(buflength > reslength)
  1364.   {
  1365.     fd->fd_Flags |= CHECKXFLAG_STRIPPED;
  1366.     PrintCHKXTxt(fd, "%ld bytes stripped", buflength-reslength);
  1367.     ret = DoGetVirus(fd, buffer, reslength);
  1368.   }
  1369.   else if(!(fd->fd_Flags & CHECKXFLAG_ADDRESS))
  1370.     ret = SaveUncrFile(fd, buffer, buflength);
  1371.  
  1372.   FreeCrunchMemList(fd, buffer);
  1373.  
  1374.   return ret;
  1375. }
  1376.  
  1377. /* Print file name */
  1378. static void PrintCHKXFile(struct FileData *fd)
  1379. {
  1380.   STRPTR name = fd->fd_Name;
  1381.   UBYTE i;
  1382.  
  1383.   if(fd->fd_Flags & CHECKXFLAG_DEBUG)
  1384.   {
  1385.     for(i = 0; i < fd->fd_ArchiveDepth; ++i)
  1386.       KPutC('*');
  1387.     KPrintf("%s\n", name);
  1388.   }
  1389.  
  1390.   if(fd->fd_LogFileFH)
  1391.   {
  1392.     for(i = 0; i < fd->fd_ArchiveDepth; ++i)
  1393.       FPutC(fd->fd_LogFileFH, '*');
  1394.     FPrintf(fd->fd_LogFileFH, "%s\n", name);
  1395.   }
  1396.   if(!(fd->fd_Flags & CHECKXFLAG_QUIET))
  1397.   {
  1398.     for(i = 0; i < fd->fd_ArchiveDepth; ++i)
  1399.       FPutC(Output(), '*');
  1400.     Printf("%s\n", name);
  1401.   }
  1402.   fd->fd_Flags |= CHECKXFLAG_NAMEPRINTED;
  1403. }
  1404.  
  1405. static void PrintCHKXErr(struct FileData *fd, LONG err)
  1406. {
  1407.   if(err)
  1408.   {
  1409.     STRPTR txt = 0, txt2;
  1410.  
  1411.     if(err > XFDERR_OFFSET)
  1412.     {
  1413.       ++fd->fd_XFDErrors;
  1414.       txt2 = "XFD-Error %ld: %s";
  1415.       err -= XFDERR_OFFSET;
  1416.       txt = xfdGetErrorText(err);
  1417.     }
  1418.     else if(err > XADERR_OFFSET)
  1419.     {
  1420.       ++fd->fd_XADErrors;
  1421.       txt2 = "XAD-Error %ld: %s";
  1422.       err -= XADERR_OFFSET;
  1423.       txt = xadGetErrorText(err);
  1424.     }
  1425.     else if(err > CHXWARN_OFFSET)
  1426.     {
  1427.       txt2 = "CheckX-Warning %ld: %s";
  1428.       switch(err)
  1429.       {
  1430.       case CHKXWARN_NOVIRUS:    txt = "Virus-Checking disabled!";                        break;
  1431.       case CHKXWARN_XVSSELFTEST:txt = "The xvs.library is modified, maybe the system is virus infected!";    break;
  1432.       case CHKXWARN_MEMVIRUS:    txt = "Your system memory was virus infected!";                    break;
  1433.       case CHKXWARN_EMPTY:    txt = "file is empty";                                break;
  1434.       }
  1435.       err -= CHXWARN_OFFSET;
  1436.     }
  1437.     else
  1438.     {
  1439.       ++fd->fd_CHKXErrors;
  1440.       txt2 = "CheckX-Error %ld: %s";
  1441.       switch(err)
  1442.       {
  1443.       case CHKXERR_NOMEMORY:    txt = "not enough memory";            break;
  1444.       case CHKXERR_EXAMINEERR:    txt = "examining failed";            break;
  1445.       case CHKXERR_OPENERR:    txt = "opening file failed";            break;
  1446.       case CHKXERR_READ:    txt = "reading failed";                break;
  1447.       case CHKXERR_SCANERR:    txt = "directory scan failed";            break;
  1448.       case CHKXERR_BREAK:    txt = "user break";                break;
  1449.       case CHKXERR_OPENDIR:    txt = "opening directory failed";        break;
  1450.       case CHKXERR_RESOURCE:    txt = "needed resource not available";        break;
  1451.       case CHKXERR_NOBOOTVIRUS:    txt = "could not check for bootblock virus";    break;
  1452.       case CHKXERR_WRITE:    txt = "writing failed";                break;
  1453.       case CHKXERR_NOVIRUS:    txt = "could not check for virus";        break;
  1454.       case CHKXERR_NOMEMORYARC:    txt = "not enough memory for full tests";    break;
  1455.       }
  1456.     }
  1457.  
  1458.     PrintCHKXTxt(fd, txt2, err, txt);
  1459.   }
  1460. }
  1461.  
  1462. /* Print type text */
  1463. static void PrintCHKXTxt(struct FileData *fd, STRPTR txt, ...)
  1464. {
  1465.   UBYTE i;
  1466.  
  1467.   if(!(fd->fd_Flags & CHECKXFLAG_NAMEPRINTED))
  1468.     PrintCHKXFile(fd);
  1469.  
  1470.   if(fd->fd_Flags & CHECKXFLAG_DEBUG)
  1471.   {
  1472.     for(i = 0; i < fd->fd_RecurseDepth; ++i)
  1473.       KPutC(' ');
  1474.     RawDoFmt(txt, &txt+1, (void (*)()) KPutC, 0);
  1475.     KPutC('\n');
  1476.   }
  1477.  
  1478.   if(fd->fd_LogFileFH)
  1479.   {
  1480.     for(i = 0; i < fd->fd_RecurseDepth; ++i)
  1481.       FPutC(fd->fd_LogFileFH, ' ');
  1482.     VFPrintf(fd->fd_LogFileFH, txt, &txt+1);
  1483.     FPutC(fd->fd_LogFileFH, '\n');
  1484.     Flush(fd->fd_LogFileFH);
  1485.   }
  1486.  
  1487.   if(!(fd->fd_Flags & CHECKXFLAG_QUIET))
  1488.   {
  1489.     for(i = 0; i < fd->fd_RecurseDepth; ++i)
  1490.       FPutC(Output(), ' ');
  1491.     VPrintf(txt, &txt+1);
  1492.     FPutC(Output(), '\n');
  1493.   }
  1494. }
  1495.  
  1496. /* Add memory to the memory list. */
  1497. static LONG AddCrunchMemList(struct FileData *fd, APTR reg, ULONG size)
  1498. {
  1499.   struct CrunchMemList *ml;
  1500.  
  1501.   if((ml = (struct CrunchMemList *) AllocMem(sizeof(struct CrunchMemList),
  1502.   MEMF_ANY)))
  1503.   {
  1504.     ml->cml_Next = fd->fd_MemList;
  1505.     ml->cml_MemoryRegion = reg;
  1506.     ml->cml_MemorySize = size;
  1507.     fd->fd_MemList = ml;
  1508.     return 0;
  1509.   }
  1510.  
  1511.   return CHKXERR_NOMEMORY;
  1512. }
  1513.  
  1514. /* Free memory from the memory list. */
  1515. static void FreeCrunchMemList(struct FileData *fd, APTR reg)
  1516. {
  1517.   struct CrunchMemList mc, *ml = &mc;
  1518.  
  1519.   if(fd->fd_Flags & CHECKXFLAG_NOFREEMEM)
  1520.     return;
  1521.  
  1522.   for(mc.cml_Next = fd->fd_MemList; ml; ml = ml->cml_Next)
  1523.   {
  1524.     if(ml->cml_Next->cml_MemoryRegion == reg)
  1525.     {
  1526.       struct CrunchMemList *m;
  1527.       m = ml->cml_Next;
  1528.       ml->cml_Next = m->cml_Next;
  1529.       FreeMem(m->cml_MemoryRegion, m->cml_MemorySize);
  1530.       FreeMem(m, sizeof(struct CrunchMemList));
  1531.     }
  1532.   }
  1533.  
  1534.   fd->fd_MemList = mc.cml_Next;
  1535. }
  1536.  
  1537. /* Save file, when SAVE option and file was crunched or linked. */
  1538. static LONG SaveUncrFile(struct FileData *fd, APTR buf, ULONG size)
  1539. {
  1540.   BPTR filefh, cd;
  1541.   LONG ret = 0, i = 0;
  1542.   UBYTE name[50];
  1543.   UBYTE nbuf[256];
  1544.  
  1545.   if(!(fd->fd_SaveDirL && (fd->fd_Flags & CHKXSAVEFLAGS)))
  1546.     return 0;
  1547.  
  1548.   sprintf(name, (fd->fd_LinkNum ? "%s.%ld" : "%s"), FilePart(fd->fd_Name),
  1549.   fd->fd_LinkNum++);
  1550.  
  1551.   cd = CurrentDir(fd->fd_SaveDirL);
  1552.  
  1553.   while(!ret && name[i])
  1554.   {
  1555.     for(;name[i] && name[i] != '/'; ++i)
  1556.       nbuf[i] = name[i];
  1557.     if(name[i] == '/')
  1558.     {
  1559.       nbuf[i] = 0;
  1560.       if((filefh = Lock(nbuf, SHARED_LOCK)))
  1561.         UnLock(filefh);
  1562.       else
  1563.       {
  1564.         if((filefh = CreateDir(nbuf)))
  1565.           UnLock(filefh);
  1566.         else
  1567.           ret = CHKXERR_OPENERR;
  1568.       }
  1569.       nbuf[i] = name[i];
  1570.       ++i;
  1571.     }
  1572.   }
  1573.  
  1574.   if(!ret)
  1575.   {
  1576.     if((filefh = Open(name, MODE_NEWFILE)))
  1577.     {
  1578.       if(Write(filefh, buf, size) != size)
  1579.         ret = CHKXERR_WRITE;
  1580.       Close(filefh);
  1581.     }
  1582.     else
  1583.       ret = CHKXERR_OPENERR;
  1584.   }
  1585.  
  1586.   CurrentDir(cd);
  1587.  
  1588.   return ret;
  1589. }
  1590.  
  1591.